package cn.seecoder.ai.utils;

import org.apache.spark.sql.Row;
import org.springframework.stereotype.Component;

import java.util.*;

/**
 * @author DingXiaoyu
 * @date 2023/4/19 21:38
 */
@Component
public class DataAnalysisUtil {
    public Map<String,Integer> intDataDistribution(List<Row> rows){
        int min=Integer.MAX_VALUE;
        int max=Integer.MIN_VALUE;
        for (Row row : rows) {
            if (row.get(0)==null){
                continue;
            }
            int t=row.getInt(0);
            min=Math.min(min,t);
            max=Math.max(max,t);
        }
        Map<String,Integer> ans=new HashMap<>();
        if (max-min<=100){
            for (Row row : rows) {
                if (row.get(0)==null){
                    continue;
                }
                int t=row.getInt(0);
                ans.put(String.valueOf(t),ans.getOrDefault(String.valueOf(t),0)+1);
            }
        }else {
            int mid=(min+max)/2;
            int one=(min+mid)/2;
            int three=(max+mid)/2;
            String first=min+"-"+(one-1);
            String second=one+"-"+(mid-1);
            String third=mid+"-"+(three-1);
            String fourth=three+"-"+max;
            for (Row row : rows) {
                if (row.get(0)==null){
                    continue;
                }
                int t=row.getInt(0);
                if (t<one){
                    ans.put(first,ans.getOrDefault(first,0)+1);
                }else if (t<mid){
                    ans.put(second,ans.getOrDefault(second,0)+1);
                }else if (t<three){
                    ans.put(third,ans.getOrDefault(third,0)+1);
                }else {
                    ans.put(fourth,ans.getOrDefault(fourth,0)+1);
                }
            }
        }
        return ans;
    }

    public Map<String,Integer> doubleDataDistribution(List<Row> rows){
        double min=Double.MAX_VALUE;
        double max=Double.MIN_VALUE;
        for (Row row : rows) {
            if (row.get(0)==null){
                continue;
            }
            double t=row.getDouble(0);
            min=Math.min(min,t);
            max=Math.max(max,t);
        }
        double mid=(min+max)/2;
        double one=(min+mid)/2;
        double three=(max+mid)/2;
        Map<String,Integer> ans=new HashMap<>();
        String first=min+"-"+one;
        String second=one+"-"+mid;
        String third=mid+"-"+three;
        String fourth=three+"-"+max;
        for (Row row : rows) {
            if (row.get(0)==null){
                continue;
            }
            double t=row.getDouble(0);
            if (t<one){
                ans.put(first,ans.getOrDefault(first,0)+1);
            }else if (t<mid){
                ans.put(second,ans.getOrDefault(second,0)+1);
            }else if (t<three){
                ans.put(third,ans.getOrDefault(third,0)+1);
            }else {
                ans.put(fourth,ans.getOrDefault(fourth,0)+1);
            }
        }
        return ans;
    }

    public Map<String,Integer> stringDataDistribution(List<Row> rows){
        Map<String,Integer> ans=new HashMap<>();
        for (Row row : rows) {
            if (row.get(0)==null){
                continue;
            }
            String t=row.getString(0);
            ans.put(t,ans.getOrDefault(t,0)+1);
        }
        return ans;
    }

    public List<Map<String,Double>> scatterPlot(List<Row> rows,String c1,String c2){
        Map<List<Double>,Integer> map=new HashMap<>();
        for (Row row : rows) {
            if (row.get(0)==null || row.get(1)==null){
                continue;
            }
            Double a=get(row.get(0));
            Double b=get(row.get(1));
            map.put(Arrays.asList(a,b),map.getOrDefault(Arrays.asList(a,b),0)+1);
        }
        List<Map.Entry<List<Double>,Integer>> l=new ArrayList<>(map.entrySet());
        l.sort((a,b)->b.getValue()-a.getValue());
        List<Map<String,Double>> ans=new ArrayList<>();
        for (int i=0;i<Math.min(10,l.size());i++){
            Map<String,Double> t=new LinkedHashMap<>();
            t.put(c1,l.get(i).getKey().get(0));
            t.put(c2,l.get(i).getKey().get(1));
            t.put("count",Double.valueOf(l.get(i).getValue()));
            ans.add(t);
        }
        return ans;
    }

    private double get(Object t){
        if (t instanceof Integer){
            return ((Integer)t).doubleValue();
        }else {
            return (Double) t;
        }
    }

    public Map<String,Double> statistics(List<Row> rows){
        List<Double> list=new ArrayList<>();
        for (Row row : rows) {
            if (row.get(0)==null){
                continue;
            }
            list.add(get(row.get(0)));
        }
        double min=Double.MAX_VALUE;
        double max=Double.MIN_VALUE;
        double total=0;
        for (Double a : list) {
            min=Math.min(min,a);
            max=Math.max(max,a);
            total+=a;
        }
        double average=total/list.size();
        double variance=0;
        for (Double a : list) {
            variance+=(a-average)*(a-average);
        }
        variance/=list.size();
        Map<String,Double> ans=new HashMap<>();
        ans.put("min",min);
        ans.put("max",max);
        ans.put("average",average);
        ans.put("variance",variance);
        ans.put("standardVariance",Math.sqrt(variance));
        return ans;
    }

    public Map<String,Double> skewnessKurtosis(List<Row> rows){
        List<Double> list=new ArrayList<>();
        for (Row row : rows) {
            if (row.get(0)==null){
                continue;
            }
            list.add(get(row.get(0)));
        }
        double total=0;
        for (Double a : list) {
            total+=a;
        }
        double average=total/list.size();
        double variance=0;
        for (Double a : list) {
            variance+=(a-average)*(a-average);
        }
        variance/=list.size();
        double s=Math.sqrt(variance);
        double skewness=0;
        double kurtosis=0;
        for (Double a : list) {
            skewness+=(a-average)*(a-average)*(a-average);
            kurtosis+=(a-average)*(a-average)*(a-average)*(a-average);
        }
        int n=list.size();
        if(n<=3){
            skewness=skewness/n/s/s/s;
            kurtosis=kurtosis/n/s/s/s/s-3;
        }
        else{
            skewness=skewness*n/(n-1)/(n-2)/s/s/s;
            kurtosis=kurtosis*n*(n+1)/(n-1)/(n-2)/(n-3)/s/s/s/s-3*(n-1)*(n-1)/(n-2)/(n-3);
        }
        Map<String,Double> ans=new HashMap<>();
        ans.put("skewness",skewness);
        ans.put("kurtosis",kurtosis);
        return ans;
    }

    public Map<String,Double> pearson(List<Row> rows){
        List<double[]> list=new ArrayList<>();
        for (Row row : rows) {
            if (row.get(0)==null || row.get(1)==null){
                continue;
            }
            list.add(new double[]{get(row.get(0)),get(row.get(1))});
        }
        double avgX=0,avgY=0;
        for (double[] doubles : list) {
            avgX+=doubles[0];
            avgY+=doubles[1];
        }
        avgX/=list.size();
        avgY/=list.size();;
        double cov=0,vax=0,vay=0;
        for (double[] doubles : list) {
            cov+=(doubles[0]-avgX)*(doubles[1]-avgY);
            vax+=(doubles[0]-avgX)*(doubles[0]-avgX);
            vay+=(doubles[1]-avgY)*(doubles[1]-avgY);
        }
        double pearson=cov/(Math.sqrt(vax)*Math.sqrt(vay));
        Map<String,Double> ans=new HashMap<>();
        ans.put("pearson",pearson);
        return ans;
    }
}
